package com.cencat.common.utils;

import lombok.Data;
import lombok.experimental.UtilityClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * 加密解密工具类
 * 提供MD5、SHA、AES、RSA等多种加密算法支持
 * 
 * @author cencat
 * @since 2024-01-01
 */
@UtilityClass

public class CryptoUtils {

    private static final Logger log = LoggerFactory.getLogger(CryptoUtils.class);

    /**
     * 默认字符编码
     */
    private static final String DEFAULT_CHARSET = "UTF-8";

    /**
     * AES算法相关常量
     */
    private static final String AES_ALGORITHM = "AES";
    private static final String AES_TRANSFORMATION = "AES/CBC/PKCS5Padding";
    private static final int AES_KEY_LENGTH = 256;
    private static final int AES_IV_LENGTH = 16;

    /**
     * RSA算法相关常量
     */
    private static final String RSA_ALGORITHM = "RSA";
    private static final String RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding";
    private static final int RSA_KEY_LENGTH = 2048;

    /**
     * MD5加密
     * 
     * @param data 待加密数据
     * @return MD5加密结果（32位小写）
     */
    public static String md5(String data) {
        if (CencatStringUtils.isBlank(data)) {
            return "";
        }
        
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] bytes = md.digest(data.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(bytes).toLowerCase();
        } catch (NoSuchAlgorithmException e) {
            log.error("MD5加密失败: {}", e.getMessage());
            return "";
        }
    }

    /**
     * MD5加密（加盐）
     * 
     * @param data 待加密数据
     * @param salt 盐值
     * @return MD5加密结果
     */
    public static String md5WithSalt(String data, String salt) {
        if (CencatStringUtils.isBlank(data)) {
            return "";
        }
        
        String saltedData = data + (CencatStringUtils.isNotBlank(salt) ? salt : "");
        return md5(saltedData);
    }

    /**
     * SHA-1加密
     * 
     * @param data 待加密数据
     * @return SHA-1加密结果
     */
    public static String sha1(String data) {
        return sha(data, "SHA-1");
    }

    /**
     * SHA-256加密
     * 
     * @param data 待加密数据
     * @return SHA-256加密结果
     */
    public static String sha256(String data) {
        return sha(data, "SHA-256");
    }

    /**
     * SHA-512加密
     * 
     * @param data 待加密数据
     * @return SHA-512加密结果
     */
    public static String sha512(String data) {
        return sha(data, "SHA-512");
    }

    /**
     * SHA加密通用方法
     * 
     * @param data 待加密数据
     * @param algorithm 算法名称
     * @return SHA加密结果
     */
    private static String sha(String data, String algorithm) {
        if (CencatStringUtils.isBlank(data)) {
            return "";
        }
        
        try {
            MessageDigest md = MessageDigest.getInstance(algorithm);
            byte[] bytes = md.digest(data.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(bytes).toLowerCase();
        } catch (NoSuchAlgorithmException e) {
            log.error("{}加密失败: {}", algorithm, e.getMessage());
            return "";
        }
    }

    /**
     * 生成AES密钥
     * 
     * @return Base64编码的AES密钥
     */
    public static String generateAesKey() {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(AES_ALGORITHM);
            keyGenerator.init(AES_KEY_LENGTH);
            SecretKey secretKey = keyGenerator.generateKey();
            return Base64.getEncoder().encodeToString(secretKey.getEncoded());
        } catch (NoSuchAlgorithmException e) {
            log.error("生成AES密钥失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 生成AES初始化向量
     * 
     * @return Base64编码的IV
     */
    public static String generateAesIv() {
        byte[] iv = new byte[AES_IV_LENGTH];
        new SecureRandom().nextBytes(iv);
        return Base64.getEncoder().encodeToString(iv);
    }

    /**
     * AES加密
     * 
     * @param data 待加密数据
     * @param key Base64编码的密钥
     * @param iv Base64编码的初始化向量
     * @return Base64编码的加密结果
     */
    public static String aesEncrypt(String data, String key, String iv) {
        if (CencatStringUtils.isBlank(data) || CencatStringUtils.isBlank(key) || CencatStringUtils.isBlank(iv)) {
            return null;
        }
        
        try {
            byte[] keyBytes = Base64.getDecoder().decode(key);
            byte[] ivBytes = Base64.getDecoder().decode(iv);
            
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
            
            Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
            
            byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(encrypted);
            
        } catch (Exception e) {
            log.error("AES加密失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * AES解密
     * 
     * @param encryptedData Base64编码的加密数据
     * @param key Base64编码的密钥
     * @param iv Base64编码的初始化向量
     * @return 解密后的原始数据
     */
    public static String aesDecrypt(String encryptedData, String key, String iv) {
        if (CencatStringUtils.isBlank(encryptedData) || CencatStringUtils.isBlank(key) || CencatStringUtils.isBlank(iv)) {
            return null;
        }
        
        try {
            byte[] keyBytes = Base64.getDecoder().decode(key);
            byte[] ivBytes = Base64.getDecoder().decode(iv);
            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
            
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, AES_ALGORITHM);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
            
            Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
            
            byte[] decrypted = cipher.doFinal(encryptedBytes);
            return new String(decrypted, StandardCharsets.UTF_8);
            
        } catch (Exception e) {
            log.error("AES解密失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 生成RSA密钥对
     * 
     * @return RSA密钥对
     */
    public static RsaKeyPair generateRsaKeyPair() {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
            keyPairGenerator.initialize(RSA_KEY_LENGTH);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            
            String publicKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
            String privateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
            
            return new RsaKeyPair(publicKey, privateKey);
            
        } catch (NoSuchAlgorithmException e) {
            log.error("生成RSA密钥对失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * RSA公钥加密
     * 
     * @param data 待加密数据
     * @param publicKey Base64编码的公钥
     * @return Base64编码的加密结果
     */
    public static String rsaEncryptByPublicKey(String data, String publicKey) {
        if (CencatStringUtils.isBlank(data) || CencatStringUtils.isBlank(publicKey)) {
            return null;
        }
        
        try {
            byte[] keyBytes = Base64.getDecoder().decode(publicKey);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PublicKey pubKey = keyFactory.generatePublic(keySpec);
            
            Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            
            byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(encrypted);
            
        } catch (Exception e) {
            log.error("RSA公钥加密失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * RSA私钥解密
     * 
     * @param encryptedData Base64编码的加密数据
     * @param privateKey Base64编码的私钥
     * @return 解密后的原始数据
     */
    public static String rsaDecryptByPrivateKey(String encryptedData, String privateKey) {
        if (CencatStringUtils.isBlank(encryptedData) || CencatStringUtils.isBlank(privateKey)) {
            return null;
        }
        
        try {
            byte[] keyBytes = Base64.getDecoder().decode(privateKey);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PrivateKey privKey = keyFactory.generatePrivate(keySpec);
            
            Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, privKey);
            
            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
            byte[] decrypted = cipher.doFinal(encryptedBytes);
            
            return new String(decrypted, StandardCharsets.UTF_8);
            
        } catch (Exception e) {
            log.error("RSA私钥解密失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * RSA私钥加密（用于数字签名）
     * 
     * @param data 待加密数据
     * @param privateKey Base64编码的私钥
     * @return Base64编码的加密结果
     */
    public static String rsaEncryptByPrivateKey(String data, String privateKey) {
        if (CencatStringUtils.isBlank(data) || CencatStringUtils.isBlank(privateKey)) {
            return null;
        }
        
        try {
            byte[] keyBytes = Base64.getDecoder().decode(privateKey);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PrivateKey privKey = keyFactory.generatePrivate(keySpec);
            
            Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, privKey);
            
            byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(encrypted);
            
        } catch (Exception e) {
            log.error("RSA私钥加密失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * RSA公钥解密（用于验证数字签名）
     * 
     * @param encryptedData Base64编码的加密数据
     * @param publicKey Base64编码的公钥
     * @return 解密后的原始数据
     */
    public static String rsaDecryptByPublicKey(String encryptedData, String publicKey) {
        if (CencatStringUtils.isBlank(encryptedData) || CencatStringUtils.isBlank(publicKey)) {
            return null;
        }
        
        try {
            byte[] keyBytes = Base64.getDecoder().decode(publicKey);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PublicKey pubKey = keyFactory.generatePublic(keySpec);
            
            Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, pubKey);
            
            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
            byte[] decrypted = cipher.doFinal(encryptedBytes);
            
            return new String(decrypted, StandardCharsets.UTF_8);
            
        } catch (Exception e) {
            log.error("RSA公钥解密失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 生成随机盐值
     * 
     * @param length 盐值长度
     * @return 随机盐值
     */
    public static String generateSalt(int length) {
        if (length <= 0) {
            length = 16;
        }
        
        byte[] salt = new byte[length];
        new SecureRandom().nextBytes(salt);
        return Base64.getEncoder().encodeToString(salt);
    }

    /**
     * 生成随机字符串
     * 
     * @param length 字符串长度
     * @return 随机字符串
     */
    public static String generateRandomString(int length) {
        if (length <= 0) {
            return "";
        }
        
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        SecureRandom random = new SecureRandom();
        StringBuilder sb = new StringBuilder(length);
        
        for (int i = 0; i < length; i++) {
            sb.append(chars.charAt(random.nextInt(chars.length())));
        }
        
        return sb.toString();
    }

    /**
     * Base64编码
     * 
     * @param data 待编码数据
     * @return Base64编码结果
     */
    public static String base64Encode(String data) {
        if (CencatStringUtils.isBlank(data)) {
            return "";
        }
        
        try {
            return Base64.getEncoder().encodeToString(data.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("Base64编码失败: {}", e.getMessage());
            return "";
        }
    }

    /**
     * Base64解码
     * 
     * @param encodedData Base64编码的数据
     * @return 解码后的原始数据
     */
    public static String base64Decode(String encodedData) {
        if (CencatStringUtils.isBlank(encodedData)) {
            return "";
        }
        
        try {
            byte[] decodedBytes = Base64.getDecoder().decode(encodedData);
            return new String(decodedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("Base64解码失败: {}", e.getMessage());
            return "";
        }
    }

    /**
     * URL安全的Base64编码
     * 
     * @param data 待编码数据
     * @return URL安全的Base64编码结果
     */
    public static String base64UrlEncode(String data) {
        if (CencatStringUtils.isBlank(data)) {
            return "";
        }
        
        try {
            return Base64.getUrlEncoder().withoutPadding().encodeToString(data.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("URL安全Base64编码失败: {}", e.getMessage());
            return "";
        }
    }

    /**
     * URL安全的Base64解码
     * 
     * @param encodedData URL安全的Base64编码数据
     * @return 解码后的原始数据
     */
    public static String base64UrlDecode(String encodedData) {
        if (CencatStringUtils.isBlank(encodedData)) {
            return "";
        }
        
        try {
            byte[] decodedBytes = Base64.getUrlDecoder().decode(encodedData);
            return new String(decodedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("URL安全Base64解码失败: {}", e.getMessage());
            return "";
        }
    }

    /**
     * 字节数组转十六进制字符串
     * 
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

    /**
     * 十六进制字符串转字节数组
     * 
     * @param hex 十六进制字符串
     * @return 字节数组
     */
    private static byte[] hexToBytes(String hex) {
        if (hex == null || hex.length() % 2 != 0) {
            return new byte[0];
        }
        
        byte[] bytes = new byte[hex.length() / 2];
        for (int i = 0; i < hex.length(); i += 2) {
            bytes[i / 2] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
        }
        return bytes;
    }

    /**
     * RSA密钥对
     */
    @Data
    public static class RsaKeyPair {
        /**
         * Base64编码的公钥
         */
        private String publicKey;

        /**
         * Base64编码的私钥
         */
        private String privateKey;

        public RsaKeyPair() {}

        public RsaKeyPair(String publicKey, String privateKey) {
            this.publicKey = publicKey;
            this.privateKey = privateKey;
        }
    }

    /**
     * AES加密结果
     */
    @Data
    public static class AesEncryptResult {
        /**
         * Base64编码的加密数据
         */
        private String encryptedData;

        /**
         * Base64编码的密钥
         */
        private String key;

        /**
         * Base64编码的初始化向量
         */
        private String iv;

        public AesEncryptResult() {}

        public AesEncryptResult(String encryptedData, String key, String iv) {
            this.encryptedData = encryptedData;
            this.key = key;
            this.iv = iv;
        }
    }

    /**
     * AES加密（自动生成密钥和IV）
     * 
     * @param data 待加密数据
     * @return AES加密结果
     */
    public static AesEncryptResult aesEncryptAuto(String data) {
        if (CencatStringUtils.isBlank(data)) {
            return null;
        }
        
        String key = generateAesKey();
        String iv = generateAesIv();
        String encryptedData = aesEncrypt(data, key, iv);
        
        if (encryptedData != null) {
            return new AesEncryptResult(encryptedData, key, iv);
        }
        
        return null;
    }

    /**
     * 密码强度检查
     * 
     * @param password 密码
     * @return 密码强度等级（0-4）
     */
    public static int checkPasswordStrength(String password) {
        if (CencatStringUtils.isBlank(password)) {
            return 0;
        }
        
        int score = 0;
        
        // 长度检查
        if (password.length() >= 8) {
            score++;
        }
        
        // 包含小写字母
        if (password.matches(".*[a-z].*")) {
            score++;
        }
        
        // 包含大写字母
        if (password.matches(".*[A-Z].*")) {
            score++;
        }
        
        // 包含数字
        if (password.matches(".*\\d.*")) {
            score++;
        }
        
        // 包含特殊字符
        if (password.matches(".*[!@#$%^&*()_+\\-=\\[\\]{};':,.<>?].*")) {
            score++;
        }
        
        return Math.min(score, 4);
    }

    /**
     * 获取密码强度描述
     * 
     * @param password 密码
     * @return 密码强度描述
     */
    public static String getPasswordStrengthDescription(String password) {
        int strength = checkPasswordStrength(password);
        switch (strength) {
            case 0:
            case 1:
                return "弱";
            case 2:
                return "一般";
            case 3:
                return "强";
            case 4:
                return "很强";
            default:
                return "未知";
        }
    }

    /**
     * 验证密码是否匹配
     * 
     * @param rawPassword 原始密码
     * @param encodedPassword 加密后的密码
     * @param salt 盐值
     * @return 是否匹配
     */
    public static boolean matches(String rawPassword, String encodedPassword, String salt) {
        if (CencatStringUtils.isBlank(rawPassword) || CencatStringUtils.isBlank(encodedPassword)) {
            return false;
        }
        
        String hashedPassword = md5WithSalt(rawPassword, salt);
        return encodedPassword.equals(hashedPassword);
    }

    /**
     * 验证密码是否匹配（使用默认盐值）
     * 
     * @param rawPassword 原始密码
     * @param encodedPassword 加密后的密码
     * @return 是否匹配
     */
    public static boolean matches(String rawPassword, String encodedPassword) {
        return matches(rawPassword, encodedPassword, "cencat_salt");
    }
}